local descriptor = require "protobuf.descriptor"
local FieldDescriptor = descriptor.FieldDescriptor
UtilTable = {}

function UtilTable.readOnly(t)
		if type(t) == "table" then
			for k,v in pairs(t) do
				t[k] = UtilTable.readOnly (v)
			end
		else
			return t
		end

		local proxy = {}
		local mt = {
			__index = t,
			__newindex = function (t, k, v)			
			end
		}
		setmetatable(proxy, mt)
		return proxy
end

--从目标数组中随即选取 {num} 个成员
function UtilTable.getRandomElems(list, targetNum)
	if #list <= targetNum then
		return list
	end
	
	local result = {}
	local i = 1
	while i <= targetNum do
		local idx = math.random(#list)		
		table.insert(result, table.remove(list, idx))
		i = i + 1
	end
	
	return result
end

function UtilTable.getTableLen(tableArr)
	local count = 0
	if type(tableArr) ~= "table" then return count end 
	for k,v in pairs(tableArr) do
		count = count + 1
	end
	return count 
end

--sort排序
function UtilTable.sort(inputT,compareFunc, num)
	if compareFunc == nil then return nil end
	if num == nil then
		num = "all"
	end
	
	local tempArr = {}
	local sortArr = {}
	for k,v in pairs(inputT) do
		table.insert(tempArr, v)
	end
	
	table.sort(tempArr,compareFunc)
	if num == "all" then
		for _, v in ipairs(tempArr) do
			table.insert(sortArr, v)
		end	
	else
		local totalnum = tonumber(num)
		for _, v in ipairs(tempArr) do
			if totalnum <= 0 then
				break
			end
			table.insert(sortArr, v)
			totalnum = totalnum - 1
		end	
	end
	return sortArr
end

function UtilTable.search(t, elem)
	for k, v in pairs(t) do
		if v == elem then
			return k
		end
	end
	
	return -1
end


-- 打印数组
-- modName: 模块名，打印是否输出由该模块调试开工控制
-- tbl：表
-- level：打印层次

UtilTable.addrCache = {}
UtilTable.tableDeep = 0
UtilTable.DEFAULT_DEEP = 5
UtilTable.maxDeep = 5
function UtilTable.table2Str(root, level)
	UtilTable.tableDeep = 0
	UtilTable.addrCache = {}
	if level then
		UtilTable.maxDeep = level
	else
		UtilTable.maxDeep = UtilTable.DEFAULT_DEEP
	end
	if root == nil then
		return "\n Invalid table:  nil value"
	end
	if type(root) ~= "table" then
		
		return "\n Invalid table: "..type(root).."\n"..tostring(root)
	end

	return "\nroot:".. type(root) .."\n" .. UtilTable.dumpString(root)
end

function UtilTable.dumpString(t)
	local temp = ""
	for k,v in pairs(t) do
		local key = tostring(k)
		if type(v) == "table" then
			if UtilTable.tableDeep > UtilTable.maxDeep then
				return temp .. "\ntoo deep------maybe table nested------table name----"..key.."\n"
			end
			UtilTable.addrCache[tostring(v)] = true
			temp = temp.."\n"..UtilTable.deepString().." +" .. key .. ":"..type(v) .. "\n"..UtilTable.deepString().."{\n"
			UtilTable.tableDeep = UtilTable.tableDeep + 1
			temp = temp .. UtilTable.dumpString(v)
			UtilTable.tableDeep = UtilTable.tableDeep - 1
			temp = temp..UtilTable.deepString().."}\n"
		else
			temp = temp..UtilTable.deepString().." +" .. key .. ":"..type(v)..":".. tostring(v).."\n"
		end
	end

	return temp.."\n"
end

function UtilTable.deepString()
	local str = ""
	for i = 1,UtilTable.tableDeep do
		str = str .. "\t"
	end
	return str
end

function UtilTable.clone(t)
	if not t then return  end
    local lookupTable = {}

    local function _copy(t)
        if type(t) ~= "table" then
            return t
        elseif lookupTable[t] then
            return lookupTable[t]
        end

        local newTable = {}
        lookupTable[t] = newTable
        for index, value in pairs(t) do
            newTable[_copy(index)] = _copy(value)
        end
        
        return setmetatable(newTable, getmetatable(t))
    end

    return _copy(t)
end

-- TODO: 测试阶段
function UtilTable.cloneGameData(t)
    local function _copy(t)
        if type(t) ~= "table" then
            return t
        else
			local newTable = {}
			for index, value in pairs(t) do
				newTable[index] = _copy(value)
			end
			local mt = getmetatable(t)
			if mt then
				local base = { __index = mt.__index}
				return setmetatable(newTable, base)
			else
				return newTable
			end
        end
    end
    return _copy(t)
end

function UtilTable.add2Table(id, num, items)
	id = tostring(id)
	num = tonumber(num)
	if items[id] then
		items[id] = items[id] + num
	else
		items[id] = num
	end
end

-- @public
-- 洗牌（随机排序），注意：暂时仅支持索引数组
-- @param table t 索引数组，注意：t中的元素会被清空
-- @return table
function UtilTable.shuffle(t)
	local shuffledT = {}
	if type(t) ~= "table" or #t < 1 then
		return shuffledT
	end

	while #t > 0 do
		table.insert(shuffledT, table.remove(t, math.random(#t)))
	end

	return shuffledT
end

function UtilTable.getRange(t, len)
	local arr = {}
	for i = 1, len do
		table.insert(arr, t[i])
	end

	return arr
end

function UtilTable.AddRange(t1, t2)
	for i = 1, #t2 do
		table.insert(t1, t2[i])
	end
end

function UtilTable.protoMsgToTable(msg)
	local data = {}
	for field, value in msg:ListFields() do
		if field.label == FieldDescriptor.LABEL_REPEATED then
			data[field.name] = {}
			for _, k in ipairs(value) do
				if field.type == FieldDescriptor.TYPE_MESSAGE then
					table.insert(data[field.name], UtilTable.protoMsgToTable(k))
				else
					table.insert(data[field.name], k)
				end
			end
		elseif field.type == FieldDescriptor.TYPE_MESSAGE then
			data[field.name] = UtilTable.protoMsgToTable(value)
		else
			data[field.name] = value
		end
	end
	return data
end



